#include <stdio.h>
#include <stdlib.h>
#include "loop.h"
#include "promise.h"

struct promise
{
    void (* func)(void *, void *);
    void * param;
    void * result;
    struct promise * then;
    int resolved;
    struct promise * prev;
    int then_free;
};

static void panic(const char * msg)
{
    puts(msg);
    exit(1);
}

static void do_promise(void * promise)
{
    struct promise * current = (struct promise *)promise;
    (current->func)(promise, current->param);
}

static void free_promises(struct promise * promise)
{
    while (promise)
    {
        puts("freeing ...");
        struct promise * prev = promise->prev;
        free(promise);
        promise = prev;
    }
}

void * new_promise(void (* func)(void *, void *), void * param)
{
    if (!func) panic("new_promise: param func is null");
    struct promise * promise = malloc(sizeof(struct promise));
    if (!promise) panic("new_promise: malloc failed");
    promise->func = func;
    promise->param = param;
    promise->then = 0;
    promise->resolved = 0;
    promise->prev = 0;
    if (!post(do_promise, promise)) panic("new_promise: queue full");
    return promise;
}

void * then_promise(void * promise, void (* func)(void *, void *))
{
    if (!promise) panic("then_promise: param promise is null");
    if (!func) panic("then_promise: param func is null");
    struct promise * current = (struct promise *)promise;
    if (current->then || current->then_free) panic("then_promise: then already set");
    struct promise * then = malloc(sizeof(struct promise));
    if (!then) panic("then_promise: malloc failed");
    then->func = func;
    then->then = 0;
    then->resolved = 0;
    then->prev = current;
    current->then = then;
    if (current->resolved)
    {
        then->param = current->result;
        if (!post(do_promise, then)) panic("then_promise: queue full");
    }
    return then;
}

void then_free_promises(void * promise)
{
    if (!promise) panic("then_free_promises: param promise is null");
    struct promise * current = (struct promise *)promise;
    if (current->then || current->then_free) panic("then_free_promises: then already set");
    current->then_free = 1;
    if (current->resolved) free_promises(current);
}

void resolve_promise(void * promise, void * result)
{
    if (!promise) panic("resolve_promise: param promise is null");
    struct promise * current = (struct promise *)promise;
    current->result = result;
    current->resolved = 1;
    if (current->then_free)
    {
        free_promises(current);
    }
    else
    {
        struct promise * then = current->then;
        if (then)
        {
            then->param = result;
            if (!post(do_promise, then)) panic("resolve_promise: queue full");
        }
    }
}
